﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using AMatysiak.Collections;
using AMatysiak.Comparers;
using AMatysiak.Controls;
using AMatysiak.Methods;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AMatysiakTest.Comparers
{
    [TestClass]
    public class MultiColumnComparerTest
    {
        private const int BIGGER = 1;
        private const int EQUAL = 0;
        private const int SMALLER = -1;

        private DataGridView grid;

        private IComparer comparer;

        private const int INT_COL = 0;
        private const int STRING_COL = 1;
        private const int DOUBLE_COL = 2;
        private const int DATE_COL = 3;
        private const int BOOL_COL = 4;
        private const int ENUM_COL = 5;

        public MultiColumnComparerTest()
        {
            grid = new DataGridView();
            comparer = MultiColumnComparer.Comparer;
            addColumns();
            generateRows();
        }

        private void addColumns()
        {
            grid.Columns.AddRange(
                new DataGridViewTextBoxColumn() { ValueType = typeof(int) },
                new DataGridViewTextBoxColumn() { ValueType = typeof(string) },
                new DataGridViewTextBoxColumn() { ValueType = typeof(double) },
                new DataGridViewCalendarColumn(),
                new DataGridViewCheckBoxColumn(),
                new DataGridViewEnumColumn() { ValueType = typeof(CheckState) }
            );
        }

        private void generateRows()
        {
            for (int i = 0; i < 20; i++)
                grid.Rows.Add(
                    i,
                    "row {0}".FormatWith(i), 
                    100.25 * i, 
                    DateTime.Now.AddDays(i), 
                    (i % 2 == 0 ? true : false), 
                    (i % 3 == 0 ? CheckState.Checked : CheckState.Unchecked)
                );
        }

        delegate bool testRowsMethod(DataGridViewRow row1, DataGridViewRow row2);

        private void makeTests(testRowsMethod test, List<MultiColumnRule> rules)
        {
            IComparer comparer = new MultiColumnComparer(rules);
            grid.Sort(comparer);

            validateRows(test);
        }

        private void validateRows(testRowsMethod test)
        {
            int rowsCount = getRowsCount();
            for (int i = 0; i < rowsCount; i++)
                Assert.IsTrue(test(grid.Rows[i], grid.Rows[i + 1]));
        }

        private int getRowsCount()
        {
            return grid.Rows.Count - 2;
        }

        private bool smallerOrEqual(int result)
        {
            if (result == SMALLER || result == EQUAL)
                return true;
            return false;
        }

        private bool biggerOrEqual(int result)
        {
            if (result == BIGGER || result == EQUAL)
                return true;
            return false;
        }

        private bool smallerThen(int result)
        {
            if (result == SMALLER)
                return true;
            return false;
        }

        private bool bothEquals(int result)
        {
            if (result == EQUAL)
                return true;
            return false;
        }

        private bool biggerThen(int result)
        {
            if (result == BIGGER)
                return true;
            return false;
        }

        [TestMethod]
        public void compareByIntegerColumnAsc()
        {
            makeTests(
                new testRowsMethod((x, y) => smallerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(INT_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByIntegerColumnDesc()
        {
            makeTests(
                new testRowsMethod((x, y) => biggerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(INT_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByStringColumnAsc()
        {
            makeTests(
                new testRowsMethod((x, y) => smallerThen(comparer.Compare(x.Cells[STRING_COL].Value, y.Cells[STRING_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(STRING_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByStringColumnDesc()
        {
            makeTests(
                new testRowsMethod((x, y) => biggerThen(comparer.Compare(x.Cells[STRING_COL].Value, y.Cells[STRING_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(STRING_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByDoubleColumnAsc()
        {
            makeTests(
                new testRowsMethod((x, y) => smallerThen(comparer.Compare(x.Cells[DOUBLE_COL].Value, y.Cells[DOUBLE_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(DOUBLE_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByDoubleColumnDesc()
        {
            makeTests(
                new testRowsMethod((x, y) => biggerThen(comparer.Compare(x.Cells[DOUBLE_COL].Value, y.Cells[DOUBLE_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(DOUBLE_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByDateTimeColumnAsc()
        {
            makeTests(
                new testRowsMethod((x, y) => smallerThen(comparer.Compare(x.Cells[DATE_COL].Value, y.Cells[DATE_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(DATE_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByDateTimeColumnDesc()
        {
            makeTests(
                new testRowsMethod((x, y) => biggerThen(comparer.Compare(x.Cells[DATE_COL].Value, y.Cells[DATE_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(DATE_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByBoolColumnAsc()
        {
            makeTests(
                new testRowsMethod((x, y) => smallerOrEqual(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByBoolColumnDesc()
        {
            makeTests(
                new testRowsMethod((x, y) => biggerOrEqual(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByEnumColumnAsc()
        {
            makeTests(
                new testRowsMethod((x, y) => smallerOrEqual(comparer.Compare(x.Cells[ENUM_COL].Value, y.Cells[ENUM_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(ENUM_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByEnumColumnDesc()
        {
            makeTests(
                new testRowsMethod((x, y) => biggerOrEqual(comparer.Compare(x.Cells[ENUM_COL].Value, y.Cells[ENUM_COL].Value))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(ENUM_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByBoolAscAndIntegerAscColumn()
        {
            makeTests(
                new testRowsMethod((x, y) => 
                    smallerThen(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) ||
                    (bothEquals(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) && 
                     smallerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value)))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(INT_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByBoolAscAndIntegerDscColumn()
        {
            makeTests(
                new testRowsMethod((x, y) =>
                    smallerThen(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) ||
                    (bothEquals(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) &&
                     biggerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value)))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(INT_COL, ListSortDirection.Descending)
                }
            );
        }

        [TestMethod]
        public void compareByBoolDescAndIntegerAscColumn()
        {
            makeTests(
                new testRowsMethod((x, y) =>
                    biggerThen(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) ||
                    (bothEquals(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) &&
                     smallerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value)))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Descending),
                    new MultiColumnRule(INT_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void compareByBoolDescAndIntegerDescColumn()
        {
            makeTests(
                new testRowsMethod((x, y) =>
                    biggerThen(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) ||
                    (bothEquals(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) &&
                     biggerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value)))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Descending),
                    new MultiColumnRule(INT_COL, ListSortDirection.Descending)
                }
            );
        }
        
        [TestMethod]
        public void compareByBoolAndEnumAndIntegerColumn()
        {
            makeTests(
                new testRowsMethod((x, y) =>
                    smallerThen(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) ||
                    (bothEquals(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) &&
                     smallerThen(comparer.Compare(x.Cells[ENUM_COL].Value, y.Cells[ENUM_COL].Value)) ||
                     (bothEquals(comparer.Compare(x.Cells[BOOL_COL].Value, y.Cells[BOOL_COL].Value)) &&
                      bothEquals(comparer.Compare(x.Cells[ENUM_COL].Value, y.Cells[ENUM_COL].Value)) &&
                      smallerThen(comparer.Compare(x.Cells[INT_COL].Value, y.Cells[INT_COL].Value))))),
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(ENUM_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(INT_COL, ListSortDirection.Ascending)
                }
            );
        }

        [TestMethod]
        public void canPassAllDifferentColumns()
        {
            MultiColumnComparer comparer = new MultiColumnComparer(
                new List<MultiColumnRule>()
                {
                    new MultiColumnRule(INT_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(STRING_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(DOUBLE_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(DATE_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(BOOL_COL, ListSortDirection.Ascending),
                    new MultiColumnRule(ENUM_COL, ListSortDirection.Ascending)
                }
            );

            int result = comparer.Compare(grid.Rows[0], grid.Rows[1]);
            Assert.AreEqual(SMALLER, result);           
        }

    }
}
